
#include <cstdio>
#include "IGlobal.h"
#include "member_BThread.h"

using namespace BWE;

#define member					(*(member_BThread*)_ptr)
#define member_allocate()		_ptr = new member_BThread(this)
#define member_release()		delete (member_BThread*)_ptr

BThread* BThread::Current()
{
#ifdef linux
	pthread_t thread_id = pthread_self();
#endif
#ifdef _WIN32
	DWORD thread_id = GetCurrentThreadId();
#endif
	BThread* thread = BApplication::thread();
	if (thread_member(thread)->thread_id == thread_id)
	{
		return thread;
	}
	for (auto it = app_threads.begin(); it.valid(); ++it)
	{
		if (BThread* thread = *it)
		{
			if (thread_member(thread)->thread_id == thread_id)
			{
				return thread;
			}
		}
	}
	return 0;
}
BUInt BThread::CurrentId()
{
#ifdef linux
	return pthread_self();
#endif
#ifdef _WIN32
	return GetCurrentThreadId();
#endif
}

#ifdef linux
#include <errno.h>

void* thead_routine(void* parameter)
{
	BThread* thread = (BThread*)parameter;
	pthread_t thread_id = thread_member(thread)->thread_id;
	while (thread && !thread_member(thread)->done)
	{
		BReal tick = bTime();
		thread_member(thread)->run();
		thread_member(thread)->frameCount++;
		if (tick - thread_member(thread)->frameTick > 1.0)
		{
			thread_member(thread)->frameTick = tick;
			thread_member(thread)->realFrequency = thread_member(thread)->frameCount;
			thread_member(thread)->frameCount = 0;
		}
		if (thread_member(thread)->frequency != 0.0)
		{
			BReal time = bTime() - tick;
			BReal sleepTime = 1.0 / thread_member(thread)->frequency - time;
			thread->sleep(sleepTime);
		}
	}
	printf("thread %lu over!\n", thread_id);
	return &(thread_member(thread)->code);
}

BThread::BThread(const BString& name) : BObject(name)
{
	member_allocate();
	app_threads.append(this);
}
BThread::~BThread()
{
	app_threads.remove(this);
	this->cease();
	member_release();
}

bool BThread::start()
{
	pthread_t thread_id = member.thread_id;
	if (member.thread_id)
	{
		if (!member.runing)
		{
			pthread_cond_signal(&member.cond);
			member.runing = true;
		}
	}
	else
	{
		if (0 != pthread_cond_init(&member.cond, 0))
		{
			puts("thread error : failed to init thread cond.");
			return false;
		}
		pthread_mutexattr_t attr;
		pthread_mutexattr_init(&attr);
		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
		if (0 != pthread_mutex_init(&member.mutex, &attr))
		{
			puts("thread error : failed to init thread cond.");
			return false;
		}
		int ret = pthread_create(&member.thread_id, 0, thead_routine, this);
		if (ret)
		{
			perror("thread error : failed to create thread.");
			return false;
		}
	}
	return true;
}
bool BThread::pause()
{
	return this->sleep(-1.0);
}

bool BThread::sleep(double seconds)
{
	if (member.thread_id == 0)
		return false;
	if (seconds == 0.0)
		return start();
	if (seconds < 0.0)
	{
		member.runing = false;
		pthread_cond_wait(&member.cond, &member.mutex);
		member.runing = true;
	}
	else
	{
		member.runing = false;
		timeval now;
		gettimeofday(&now, NULL);
		timespec abstime;
		abstime.tv_sec = now.tv_sec + (long)seconds;
		abstime.tv_nsec = now.tv_usec * 1000 + long((seconds - (long)seconds) * 1000000000.0);
		if (abstime.tv_nsec > 1000000000)
		{
			abstime.tv_sec++;
			abstime.tv_nsec -= 1000000000;
		}
		int ret = pthread_cond_timedwait(&member.cond, &member.mutex, &abstime);
		if (ret == EINVAL)
			perror("The value specified by abstime is invalid.");
		if (ret == EPERM)
			perror("The mutex was not owned by the current thread at the time of the call.");
		//::usleep(seconds * 1000000.0);
		member.runing = true;
	}
	return true;
}

bool BThread::wait()
{
	int ret = pthread_cond_wait(&member.cond, &member.mutex);
	if (ret)
	{
		puts("error : thread condition wait failed.");
		return false;
	}
	return true;
}
bool BThread::cease()
{
	if (member.thread_id)
	{
		pthread_cond_signal(&member.cond);
		pthread_cond_destroy(&member.cond);
		pthread_mutex_destroy(&member.mutex);
		pthread_detach(member.thread_id);
		return true;
	}
	return false;
}

bool BThread::done(int code)
{
	if (member.thread_id && !member.done)
	{
		member.done = true;
		member.code = code;
		if (!member.runing)
		{
			pthread_cond_signal(&member.cond);
		}
		emit(Signal_Done, code);
		return true;
	}
	return false;
}

#endif

#ifdef _WIN32

DWORD WINAPI thread_routine(void* param)
{
	BThread* thread = (BThread*)param;
	while (thread && !thread_member(thread)->done)
	{
		BReal tick = bTime();
		thread_member(thread)->run();
		thread_member(thread)->frameCount++;
		if (tick - thread_member(thread)->frameTick > 1.0)
		{
			thread_member(thread)->frameTick = tick;
			thread_member(thread)->realFrequency = thread_member(thread)->frameCount;
			thread_member(thread)->frameCount = 0;
		}
		int frequency = thread_member(thread)->frequency;
		if (frequency < 0)
		{
			thread->pause();
		}
		if (frequency > 0)
		{
			BReal time = bTime() - tick;
			BReal interval = BReal(1) / frequency;
			BReal sleepTime = interval - time;
			if (sleepTime > 0)
			{
				thread->sleep(sleepTime);
			}
		}
	}
	return thread_member(thread)->code;
}

BThread::BThread(const BString& name) : BObject(name)
{
	member_allocate();
	app_threads.append(this);
}
BThread::~BThread()
{
	app_threads.remove(this);
	this->cease();
	member_release();
}

bool BThread::start()
{
	if (!member.handle)
	{
		member.handle = ::CreateThread(0, 0, thread_routine, this, CREATE_SUSPENDED, &member.thread_id);
	}
	if (member.handle)
	{
		if (!member.runing)
		{
			DWORD ret = ResumeThread(member.handle);
			member.runing = true;
		}
		return true;
	}
	return false;
}
bool BThread::pause()
{
	if (member.handle)
	{
		if (member.runing)
		{
			member.runing = false;
			DWORD ret = SuspendThread(member.handle);
		}
		return true;
	}
	return false;
}
bool BThread::sleep(BReal seconds)
{
	if (member.runing)
	{
		if (seconds == 0)
		{
			return start();
		}
		if (seconds < 0)
		{
			member.runing = SuspendThread(member.handle);
			return !member.runing;
		}
		else
		{
			if (member.htimer == 0)
				member.htimer = ::CreateWaitableTimer(0, true, 0);

			if (member.htimer == 0)
				return false;

			LARGE_INTEGER time;
			time.QuadPart = LONGLONG(seconds * -10000000);

			if (::SetWaitableTimer(member.htimer, &time, 0, 0, 0, 0) == false)
				return false;

			if (::WaitForSingleObject(member.htimer, INFINITE) == WAIT_FAILED)
				return false;
		}
	}
	return true;
}
bool BThread::wait()
{
	DWORD ret = WaitForSingleObject(member.handle, INFINITE);
	return ret != WAIT_FAILED;
}
bool BThread::cease()
{
	if (member.handle)
	{
		TerminateThread(member.handle, 0);
		::CloseHandle(member.handle);
		member.handle = 0;
		if (member.htimer)
		{
			::CloseHandle(member.htimer);
			member.htimer = 0;
		}
		return true;
	}
	return false;
}

bool BThread::done(int code)
{
	AutoLock lock(this);
	if (member.handle && !member.done)
	{
		member.done = true;
		member.code = code;
		if (!member.runing)
		{
			DWORD ret = SuspendThread(member.handle);
			member.runing = true;
		}
		emit(Signal_Done, code);
		return true;
	}
	return false;
}

#endif

void BThread::setFrequency(int frequency)
{
	AutoLock lock(this);
	if (member.frequency != frequency)
	{
		member.frequency = frequency;
	}
}
int BThread::frequency() const
{
	AutoLock lock(this);
	return member.frequency;
}
int BThread::realFrequency() const
{
	AutoLock lock(this);
	return member.realFrequency;
}

BUInt BThread::id() const
{
	return member.thread_id;
}

bool BThread::runing() const
{
	return member.runing;
}

bool BThread::query(const BString& text, BValue& value) const
{
	if (text == "thread-id")
	{
#ifdef linux
		value = member.thread_id;
#endif
#ifdef _WIN32
		value = member.thread_id;
#endif
		return true;
	}
	if (text == "frequency")
	{
		value = member.frequency;
		return true;
	}
	if (text == "real-frequency")
	{
		value = member.realFrequency;
		return true;
	}
	return BObject::query(text, value);
}
